#include "BumlsPainter.h"
#include "../util/StringOps.h"

BumlsPainter::BumlsPainter(QPainter& painter)
	: _painter(painter)
{
}

/**
 * @return a map of strings -> characters for replacement when painting
 */
std::map<QString, QChar> makeReplaceMap()
{
	using std::map;

	map<QString, QChar> ret;

	ret["<<"] = L'«';
	ret[">>"] = L'»';

	return ret;
}

const std::map<QString, QChar>& BumlsPainter::subs()
{
	static const std::map<QString, QChar> replacements = makeReplaceMap();

	return replacements;
}

/**
 * @return the first character if it's the same as the last character, otherwise
 * '\0'
 */
QChar startAndEnd(const QString& s)
{
	if (s[0] == s[s.size() - 1])
		return s[0];
	else
		return '\0';
}

/**
 * Returns a /s/ without the first and last characters, or a null string if /s/
 * has less than two characters.
 */
QString trimStartAndEnd(const QString& s)
{
	// Return null string per contract
	if (s.size() < 2)
		return QString();

	return s.mid(1, s.size() - 2);
}

/**
 * Draws the given text in the provided rectangle, applying basic rich-text
 * formatting. (See doc/bumls.txt.)
 */
void BumlsPainter::drawText(const QRectF& rect, QString text)
{
	/** First Pass: Replace Characters **/
	StringOps::substitute(text, subs());

	/**
	 * Second Pass: draw the text line-by-line, applying formatting as we go.
	 */

	// smallRect: provides rectangle for single line
	QRectF smallRect = rect;
	const QFont initialFont = _painter.font();
	QStringList lines = text.split('\n');
	for (QStringList::const_iterator line_iter = lines.begin();
			line_iter != lines.end(); ++line_iter)
	{
		QRectF outRect;

		// Copy the text so we can modify it.
		QString lineText = *line_iter;

		// Quit now if our destination Rect is invalid. (This can happen if the
		// caller provided a bad rect or if we ran out of vertical space.)
		if (!smallRect.isValid())
			break;

		// determine the font
		switch (startAndEnd(lineText).unicode())
		{
			case '/':
				{
					QFont italic = initialFont;
					italic.setItalic(true);
					_painter.setFont(italic);
					lineText = trimStartAndEnd(lineText);
				}
				break;
			case '*':
				{
					QFont bold = initialFont;
					bold.setBold(true);
					_painter.setFont(bold);
					lineText = trimStartAndEnd(lineText);
				}
				break;
			case '_':
				{
					QFont underline = initialFont;
					underline.setUnderline(true);
					_painter.setFont(underline);
					lineText = trimStartAndEnd(lineText);
				}
				break;
			default:
				break; // do nothing, font is already cleared
		}

		// draw the line
		if (lineText == "--")   // Is it a horizontal rule?
		{
			// It's an hr, make our own outRect
			outRect = smallRect;
			outRect.setHeight(5);

			// Draw the line at vertical midpoint of outRect
			QLineF hr(outRect.topLeft(), outRect.topRight());
			hr.translate(0, 2);
			_painter.drawLine(hr);
		}
		else
		{
			// Just text...
			_painter.drawText(smallRect, Qt::AlignTop, lineText, &outRect);
		}

		// Clear any font effects we may have set before. Do this at the end of
		// the loop so that we exit the function in the same state we started.
		_painter.setFont(initialFont);

		// Use the out parameter to create next smallRect:
		// - Leave left, right the same.
		// - Top: old bottom
		// - Bottom: bottom of "parent" rect
		smallRect.setTop(outRect.bottom());
		// Already done: smallRect.setBottom(rect.bottom());
	}
}

